package slist

import (
	"sync"
)

// List is goroutine safe slice
type List[T any] struct {
	mutex sync.RWMutex
	data  []T
}

func New[T any](len, cap int) *List[T] {
	p := new(List[T])
	p.data = make([]T, len, cap)
	return p
}

func (p *List[T]) Append(data ...T) {
	p.mutex.Lock()
	defer p.mutex.Unlock()
	p.data = append(p.data, data...)
}

func (p *List[T]) Get(i int) T {
	p.mutex.RLock()
	defer p.mutex.RUnlock()
	return p.data[i]
}

// GetDefault return the value place at position i
// when i doesn't exist in list, then return v without panic
func (p *List[T]) GetDefault(i int, v T) T {
	p.mutex.RLock()
	defer p.mutex.RUnlock()
	if i < 0 || i >= len(p.data) {
		return v
	}
	return p.data[i]
}

func (p *List[T]) Set(i int, d T) {
	p.mutex.Lock()
	defer p.mutex.Unlock()
	p.data[i] = d
}

// Remove removes elements which index from i to i+num
// if i is -1 will remove the last element
// if num is -1 will remove all elements which index after i
// example: Remove(-1, 1) will remove the last element
func (p *List[T]) Remove(i int, num int) {
	p.mutex.Lock()
	defer p.mutex.Unlock()
	if i < 0 {
		i = len(p.data) - 1
	}
	if i >= len(p.data) {
		return
	}
	if num == 0 {
		return
	}

	if num < 0 || i+num >= len(p.data) {
		p.data = p.data[:i]
	} else {
		p.data = append(p.data[:i], p.data[i+num:]...)
	}
}

func (p *List[T]) Clear() {
	p.mutex.Lock()
	defer p.mutex.Unlock()
	p.data = make([]T, 0)
}

func (p *List[T]) Len() int {
	p.mutex.RLock()
	defer p.mutex.RUnlock()
	return len(p.data)
}

// First return the first element of p
// if p.Len=0, then ok is false
func (p *List[T]) First() (ret T, ok bool) {
	p.mutex.RLock()
	defer p.mutex.RUnlock()
	if len(p.data) > 0 {
		ret = p.data[0]
		ok = true
	}
	return
}

// Last return the last element of p
// if p.Len=0, then ok is false
func (p *List[T]) Last() (ret T, ok bool) {
	p.mutex.RLock()
	defer p.mutex.RUnlock()
	if len(p.data) > 0 {
		ret = p.data[len(p.data)-1]
		ok = true
	}
	return
}

func (p *List[T]) Clone() (data []T) {
	p.mutex.RLock()
	defer p.mutex.RUnlock()
	data = make([]T, len(p.data), cap(p.data))
	for i, one := range p.data {
		data[i] = one
	}
	return
}

// Range iter element of p with f
// if f return false, then break the range
// note: do not call any p's function inside f, otherwise will occur deadlock
func (p *List[T]) Range(f func(index int, item T) bool) {
	p.mutex.RLock()
	defer p.mutex.RUnlock()
	for i, one := range p.data {
		if f(i, one) == false {
			break
		}
	}
}
